package org.wevil.message;

import lombok.Getter;
import lombok.Setter;
import org.wevil.util.CRC16Util;
import org.wevil.util.DateUtil;
import org.wevil.util.RandomUtil;

/**
 * 651协议，ASCII 字符编码 M2/M4 模式下行帧结构定义
 *
 * @since 2022/4/7
 * */
@Getter
@Setter
public class Message651DN {

    /** 帧起始符。 1个字节， 默认为 */
    private static String SOH = "\u0001";

    /**
     * 遥测站地址 10个字节，转换为 10 个 ASCII 字符传输。
     * */
    private String rtuAddress;

    /** 中心站地址 2个字节，1 字节 HEX 转换为 2 个 ASCII 字符传输，范围为 1～255。
     指以省（或流域机构）为单元，为县、市级以上分中心分配的中心站地址。这里默认为01
     */
    private static String centerAddress = "01";

    /**
     * 密码 4个字节  2字节HEX编码，转换为4个ASCII字符传输
     * 默认为0000
     * */
    private static String password = "0000";

    /** 功能码 2个字节 需将 1 个字节功能码转换为 2 个 ASCII 字符传输。
     * 2F -> 链路维持报。遥测站定时向中心站发送链路维持信息
     * 33 -> 遥测站加报报。报送由时间或事件触发的加报实时数据
     * 42 -> 中心站修改遥测站运行参数配置表。遥测站运行参数配置
     * 43 -> 中心站读取遥测站运行参数配置表/遥测站自报运行参数配置表*/
    private String functionCode;

    /** 报文标识及长度 用 2 字节 HEX 编码转换为 4 个 ASCII 字符传输。
     * 第 1 个字符用作上下行标识（0 表示上行，8 表示下行）；
     * 其余3 个字符表示报文正文长度，表示报文起始符之后、报
     文结束符之前的报文字节数，允许长度为 0001～4095。*/
    private String length;

    /** 报文起始符 1个字节 默认为 */
    private static String STX = "\u0002";

    /** 报文正文 长度不定 自报树、响应帧内容等
     * 心跳报文内容：
     * 流水号。2字节HEX码，范围1～65535
     * 发报时间。6字节BCD码，YYMMDDHHmmSS
     * 举例：4D3D220318120033
     * */
    private String content;

    /** 报文结束符 1个字节 默认为 */
    private static String EOT = "\u0004";

    /** 校验码 4个字节 2字节HEX编码，转换为4个ASCII字符传输；校验码
     前所有字节的 CRC 校验，生成多项式：X16+X15+X2+1，高位字节在前，低位字节在后。*/
    private String crc;

    /**
     * 根据上行报文获取下行回复心跳报文
     * */
    public static String getHeart(Message651UP message651UP){
        String rtuAddress = message651UP.getRtuAddress(); // rtu编码
        String centerAddress = message651UP.getCenterAddress(); // 中心站地址
        String password = message651UP.getPassword(); // 密码
        String functionCode = message651UP.getFunctionCode(); // 功能码
        String length = message651UP.getLength();
        length = 8 + length.substring(1); // 密码
        Integer i = RandomUtil.randomInt(0, 65535);
        byte[] bytes1 = CRC16Util.intTo2Byte(i);
        String seq = CRC16Util.encodeHexStr(bytes1).toUpperCase(); // 随机序列号
        String dateStr = DateUtil.getDateStr("yyMMddHHmmss"); // 时间戳
        String content = Message651DN.SOH +
                rtuAddress +
                centerAddress +
                password +
                functionCode +
                length +
                Message651DN.STX +
                seq + dateStr +
                Message651DN.EOT;
        int i1 = CRC16Util.CRC16_MODBUS(content.getBytes());
        byte[] bytes2 = CRC16Util.intTo2Byte(i1);
        String str = CRC16Util.encodeHexStr(bytes2).toUpperCase(); // CRC校验码
        return content + str;
    }

    /**
     * 生成查询开度的报文
     *
     * @since 2022/4/12
     * */
    public static String getOpen(String rtuAddress){
        String centerAddress = Message651DN.centerAddress; // 中心站地址
        String password = Message651DN.password; // 密码
        String functionCode = "43"; // 功能码
        String length = "8015";     // 长度, 下行查询开度报文长度固定为21个字节，对应十六进制为15
        // 内容 = 序列号(4) + 时间戳(12) + FF26(4) + 空格(1)
        Integer i = RandomUtil.randomInt(0, 65535);
        byte[] bytes1 = CRC16Util.intTo2Byte(i);
        String seq = CRC16Util.encodeHexStr(bytes1).toUpperCase(); // 随机序列号
        String dateStr = DateUtil.getDateStr("yyMMddHHmmss"); // 时间戳
        String content = seq + dateStr + "FF26 ";

        // 报文整体内容
        String message = Message651DN.SOH +
                rtuAddress +
                centerAddress +
                password +
                functionCode +
                length +
                Message651DN.STX +
                content +
                Message651DN.EOT;
        int i1 = CRC16Util.CRC16_MODBUS(message.getBytes());
        byte[] bytes2 = CRC16Util.intTo2Byte(i1);
        String str = CRC16Util.encodeHexStr(bytes2).toUpperCase(); // CRC校验码
        return message + str;
    }

    /**
     * 生成设置开度的报文
     *
     * @since 2022/4/12
     * */
    public static String setOpen(String rtuAddress, Integer open){
        String centerAddress = Message651DN.centerAddress; // 中心站地址
        String password = Message651DN.password; // 密码
        String functionCode = "42"; // 功能码
        // 长度, 根据integer的长度来确定，
        // 100的话长度为25, 对应16进制为19
        // 10-99的话长度为24， 对应16进制为18
        // 0-9的话长度为23，对应16进制为17
        String length = "8015";
        if(open == 100){
            length = "8019";
        }
        if(open <= 99 && open >= 10){
            length = "8018";
        }
        if(open <= 9 && open >= 0){
            length = "8017";
        }
        // 内容 = 序列号(4) + 时间戳(12) + FF26(4) + 空格(1) + 开度(1-3) + 空格(1)
        Integer i = RandomUtil.randomInt(0, 65535);
        byte[] bytes1 = CRC16Util.intTo2Byte(i);
        String seq = CRC16Util.encodeHexStr(bytes1).toUpperCase(); // 随机序列号
        String dateStr = DateUtil.getDateStr("yyMMddHHmmss"); // 时间戳
        String content = seq + dateStr + "FF26 " + open + " ";
        // 报文整体内容
        String message = Message651DN.SOH +
                rtuAddress +
                centerAddress +
                password +
                functionCode +
                length +
                Message651DN.STX +
                content +
                Message651DN.EOT;
        int i1 = CRC16Util.CRC16_MODBUS(message.getBytes());
        byte[] bytes2 = CRC16Util.intTo2Byte(i1);
        String str = CRC16Util.encodeHexStr(bytes2).toUpperCase(); // CRC校验码
        return message + str;
    }
}
